﻿#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/imgproc_c.h>  

#include <iostream>
#include <string>
#include <vector>

using namespace std;
using namespace cv;

double mx = 0, my = 0;
int dx = 0, dy = 0, horizBar_x = 0, vertiBar_y = 0;
bool clickVertiBar = false, clickHorizBar = false, needScroll = false;
Rect rect_bar_horiz, rect_bar_verti;
const int window_width = 1024;
const int window_height = 1024;

void help()
{
    std::cout <<
        std::endl <<
        "This program demonstrated the use of the cvSetMouseCallback" << std::endl <<
        "for viewing large image with scroll bar in a small window" << std::endl <<
        "created by OpenCV highgui model. (chenyusiyuan, 2011-06-24)" << std::endl;
}


void mouse_callback(int event, int x, int y, int flags, void* param)
{
    if (needScroll)
    {
        switch (event)
        {
        case EVENT_LBUTTONDOWN:
            mx = x, my = y;
            dx = 0, dy = 0;
            // 按下左键时光标定位在水平滚动条区域内  
            if (x >= rect_bar_horiz.x && x <= rect_bar_horiz.x + rect_bar_horiz.width
                && y >= rect_bar_horiz.y && y <= rect_bar_horiz.y + rect_bar_horiz.height)
            {
                clickHorizBar = true;
            }
            // 按下左键时光标定位在垂直滚动条区域内  
            if (x >= rect_bar_verti.x && x <= rect_bar_verti.x + rect_bar_verti.width
                && y >= rect_bar_verti.y && y <= rect_bar_verti.y + rect_bar_verti.height)
            {
                clickVertiBar = true;
            }
            break;
        case EVENT_MOUSEMOVE:
            if (clickHorizBar)
            {
                dx = fabs(x - mx) > 1 ? (int)(x - mx) : 0;
                dy = 0;
            }
            if (clickVertiBar)
            {
                dx = 0;
                dy = fabs(y - my) > 1 ? (int)(y - my) : 0;
            }
            mx = x, my = y;
            break;
        case EVENT_LBUTTONUP:
            mx = x, my = y;
            dx = 0, dy = 0;
            clickHorizBar = false;
            clickVertiBar = false;
            break;
        default:
            dx = 0, dy = 0;
            break;
        }
    }
}

void myShowImageScroll(const std::string& window_name,const Mat& src_img)
{
    Mat dst_img, dstROI;
    //窗口中有效的图像显示区域
    Rect rect_dst, rect_src;// 窗口图像对应于源图像中的区域
    int imgWidth = src_img.cols, imgHeight = src_img.rows,barWidth = 20;// 滚动条的宽度（像素）
    int winWidth = window_width;
    int winHeight = window_height;
    double scale_w = (double)imgWidth/(double)winWidth,// 源图像与窗口的宽度比值  用以判断是否超出显示范围
        scale_h = (double)imgHeight / (double)winHeight;// 源图像与窗口的高度比值  用以判断是否超出显示范围


    if (scale_w < 1)//如果小于1 说明原图比窗口小，窗口的宽度将重新赋值
        winWidth = imgWidth + barWidth;
    if (scale_h < 1)//如果小于1 说明原图比窗口小，窗口的高度将重新赋值
        winHeight = imgHeight + barWidth;


    int showWidth = winWidth, showHeight = winHeight; // 窗口中有效的图像显示区域的宽和高  
    int src_x = 0, src_y = 0;// 源图像中 rect_src 的左上角位置
    int horizBar_width = 0, horizBar_height = 0,//定义并初始化垂直于水平滑块的宽高
        vertiBar_width = 0, vertiBar_height = 0;


    needScroll = scale_w > 1.0 || scale_h > 1.0 ? true : false;
    // 若图像大于设定的窗口大小，则显示滚动条  
    if (needScroll)
    {
        dst_img = Mat(Size(winWidth, winHeight), src_img.type());
        // 源图像宽度大于窗口宽度，则显示水平滚动条  
        if (scale_w > 1.0) //宽度超出了
        {
            showHeight = winHeight - barWidth;
            horizBar_width = (int)((double)winWidth / scale_w);
            horizBar_height = winHeight - showHeight;
            horizBar_x = min(
                max(0, horizBar_x + dx),
                winWidth - horizBar_width);
            rect_bar_horiz = Rect(
                horizBar_x,
                showHeight + 1,
                horizBar_width,
                horizBar_height);// 显示水平滚动条
            rectangle(dst_img, rect_bar_horiz, cvScalarAll(255), -1);
        }
        // 源图像高度大于窗口高度，则显示垂直滚动条  
        if (scale_h > 1.0)//高度超出了
        {
            showWidth = winWidth - barWidth;
            vertiBar_width = winWidth - showWidth;
            vertiBar_height = (int)((double)winHeight / scale_h);
            vertiBar_y = min(
                max(0, vertiBar_y + dy),
                winHeight - vertiBar_height);
            rect_bar_verti = Rect(
                showWidth + 1,
                vertiBar_y,
                vertiBar_width,
                vertiBar_height);//确定垂直滚动条的白色部分的大小
                                 // 显示垂直滚动条
            rectangle(dst_img, rect_bar_verti, cvScalarAll(255), -1);
        }


        showWidth = min(showWidth, imgWidth);
        showHeight = min(showHeight, imgHeight);


        // 设置窗口显示区的 ROI  
        rect_dst = Rect(0, 0, showWidth, showHeight);
        dstROI = dst_img(rect_dst);


        // 设置源图像的 ROI  
        src_x = (int)((double)horizBar_x * scale_w);
        src_y = (int)((double)vertiBar_y * scale_h);
        src_x = min(src_x, imgWidth - showWidth);
        src_y = min(src_y, imgHeight - showHeight);
        rect_src = Rect(src_x, src_y, showWidth, showHeight);
        // 将源图像内容复制到窗口显示区  


        src_img(rect_src).convertTo(dstROI, dstROI.type());


        // 显示图像和滚动条  
        imshow(window_name, dst_img);
    }
    // 源图像小于设定窗口，则直接显示图像，无滚动条  
    else
    {
        imshow(window_name, src_img);
    }
}

int main()
{
    help();
    const std::string window_name("Image Scroll Bar");
    std::string dirname = "D:\\Program_Data\\NO_ANOTATIONS\\";
    std::string filename = "GF3_KAS_SL_012014_W75.5_N39.1_20181121_L1A_VV_L10003614431_0.020_00.tif";
    std::string fullname = dirname + filename;
    namedWindow(window_name, 1);
    setMouseCallback(window_name, mouse_callback);

    Mat image = imread(fullname, cv::IMREAD_UNCHANGED);
    if (image.data==NULL)
    {
        fprintf(stderr, "Can not load %s and/or %s/n"
            "Usage: Image_ScrollBar [<img_filename default im.jpg>]/n",
            fullname);
        exit(-1);
    }

    while (1)
    {
        myShowImageScroll(window_name, image);

        int KEY = waitKey(10);
        if ((char)KEY == 27)
            break;
    }

    return 0;
}